/*
   Copyright (c) 2020, zoomdy
   MicroAmpereII is licensed under Mulan PSL v2.
   You can use this software according to the terms and conditions of the Mulan PSL v2.
   You may obtain a copy of Mulan PSL v2 at:
            http://license.coscl.org.cn/MulanPSL2
   THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
   EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
   MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
   See the Mulan PSL v2 for more details.
 */
#include "pt/pt.h"
#include "pt/timer.h"
#include "pt/delay.h"
#include "hal_senser.h"
#include "hal_cdc.h"
#include "app_senser.h"

#define ADC_CALIB_COUNT_MAX     1000

typedef enum
{
    APP_SENSER_STATE_IDLE,
    APP_SENSER_STATE_SAMPLE,
    APP_SENSER_STATE_CALIB,
}app_senser_state_t;

typedef struct
{
    struct pt pt;
    struct timer timer;
    app_senser_state_t state;
    bool ok;
    int32_t ain_zero;
    uint32_t adc_calib_count;
    hal_cdc_req_t cdc_send_req;
    uint8_t cdc_send_buffer[HAL_CDC_PACKET_SIZE];
    uint32_t cdc_send_length;
    hal_cdc_req_t cdc_recv_req;
    uint8_t cdc_recv_buffer[HAL_CDC_PACKET_SIZE];
    uint32_t cdc_recv_length;
}app_senser_t;

static app_senser_t app_senser;

void app_senser_init(void)
{
    app_senser_t* self = &app_senser;
    self->state = APP_SENSER_STATE_IDLE;
    self->ain_zero = 0;
    self->adc_calib_count = 0;
    self->cdc_send_length = 0;

    PT_INIT(&self->pt);
}

PT_THREAD(app_senser_task(void))
{
    app_senser_t* self = &app_senser;
    uint16_t reg;
    int32_t ain;
    char cmd;

    PT_BEGIN(&self->pt);

    self->cdc_recv_req.buffer = self->cdc_recv_buffer;
    self->cdc_recv_req.length = sizeof(self->cdc_recv_buffer);

    hal_cdc_recv(&self->cdc_recv_req);

    while(1) {
        self->ok = hal_senser_fifo_get(&reg);
        if(self->ok) {
            ain = reg;
            if(self->state == APP_SENSER_STATE_CALIB) {
                self->ain_zero += ain;
                self->adc_calib_count++;
                if(self->adc_calib_count == ADC_CALIB_COUNT_MAX) {
                    self->ain_zero /= ADC_CALIB_COUNT_MAX;
                    self->state = APP_SENSER_STATE_SAMPLE;
                }
            } else if(self->state == APP_SENSER_STATE_SAMPLE){
                ain = ain - self->ain_zero;
                ain = ain < 0 ? -ain : ain;
                ain = ain * 3300 / 4096;

                self->cdc_send_buffer[self->cdc_send_length] = 0x80 | ((ain >> 6) & 0x3f);
                self->cdc_send_length++;
                self->cdc_send_buffer[self->cdc_send_length] = 0x40 | (ain & 0x3f);;
                self->cdc_send_length++;

                if(self->cdc_send_length == HAL_CDC_PACKET_SIZE) {
                    self->cdc_send_req.buffer = self->cdc_send_buffer;
                    self->cdc_send_req.length = self->cdc_send_length;
                    hal_cdc_send(&self->cdc_send_req);
                    PT_WAIT_UNTIL(&self->pt, hal_cdc_error(&self->cdc_send_req) != HAL_ERR_INPROGRESS);
                    self->cdc_send_length = 0;
                }
            } else {

            }
        }

        if(hal_cdc_error(&self->cdc_recv_req) != HAL_ERR_INPROGRESS) {
            cmd = (char)self->cdc_recv_buffer[0];
            if(cmd == 'z' || cmd == 'Z') {
                hal_senser_set_chnl(HAL_SENSER_CHNL_NULL);
                PT_DELAY(&self->pt, &self->timer, 1000);

                self->ain_zero = 0;
                self->adc_calib_count = 0;
                self->state = APP_SENSER_STATE_CALIB;
            } else if(cmd == 'e' || cmd == 'E') {
                hal_senser_set_chnl(HAL_SENSER_CHNL_0R1);
                self->state = APP_SENSER_STATE_SAMPLE;
            } else if(cmd == 'd' || cmd == 'D') {
                hal_senser_set_chnl(HAL_SENSER_CHNL_NULL);
                self->state = APP_SENSER_STATE_IDLE;
                if(self->cdc_send_length > 0) {
                    self->cdc_send_req.buffer = self->cdc_send_buffer;
                    self->cdc_send_req.length = self->cdc_send_length;
                    hal_cdc_send(&self->cdc_send_req);
                    PT_WAIT_UNTIL(&self->pt, hal_cdc_error(&self->cdc_send_req) != HAL_ERR_INPROGRESS);
                    self->cdc_send_length = 0;
                }
            } else {

            }

            self->cdc_recv_req.buffer = self->cdc_recv_buffer;
            self->cdc_recv_req.length = sizeof(self->cdc_recv_buffer);

            hal_cdc_recv(&self->cdc_recv_req);
        }

        PT_YIELD(&self->pt);
    }

    PT_END(&self->pt);
}
